﻿using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using MugenMvvmToolkit.Interfaces;
using MugenMvvmToolkit.Models;
using MugenMvvmToolkit.Utils;

namespace MugenMvvmToolkit.Infrastructure
{
    /// <summary>
    ///     Represent the factory for create <see cref="IValidator" />.
    /// </summary>
    public class ValidatorProvider : IValidatorProvider
    {
        #region Fields

        private readonly object _locker = new object();
        private IDictionary<Type, List<IValidator>> _validatorPrototypes;

        #endregion

        #region Constructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="ValidatorProvider" /> class.
        /// </summary>
        public ValidatorProvider([CanBeNull]IServiceProvider serviceProvider)
        {
            ServiceProvider = serviceProvider;
            _validatorPrototypes = new Dictionary<Type, List<IValidator>>();
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the service provider.
        /// </summary>
        public IServiceProvider ServiceProvider { get; set; }

        #endregion

        #region Implementation of IValidatorProvider

        /// <summary>
        ///     Registers the specified validator use type.
        /// </summary>
        /// <typeparam name="TValidator">The type of validator.</typeparam>
        public void Register<TValidator>() where TValidator : IValidator
        {
            IValidator validator;
            if (ServiceProvider != null)
                validator = (IValidator)ServiceProvider.GetService(typeof(TValidator));
            else
                validator = (IValidator)Activator.CreateInstance(typeof(TValidator));
            Register(validator);
        }

        /// <summary>
        ///     Registers the specified validator.
        /// </summary>
        /// <param name="validator">The specified validator</param>
        public void Register(IValidator validator)
        {
            Should.NotBeNull(validator, "validator");
            using (DeadLockDetector.Lock(_locker))
            {
                Type type = validator.GetType();
                List<IValidator> validators;
                _validatorPrototypes.TryGetValue(type, out validators);
                if (validators != null && validators.Count != 0 && validator.IsUnique)
                    Thrower.DuplicateValidator(type);
                if (validators == null)
                    validators = new List<IValidator>();
                validators.Add(validator);
                _validatorPrototypes[type] = validators;
            }
        }

        /// <summary>
        ///     Unregisters the specified validator use type.
        /// </summary>
        /// <typeparam name="TValidator">The type of validator.</typeparam>
        public bool Unregister<TValidator>() where TValidator : IValidator
        {
            using (DeadLockDetector.Lock(_locker))
            {
                return _validatorPrototypes.Remove(typeof(TValidator));
            }
        }

        /// <summary>
        ///     Determines whether the specified validator is registered
        /// </summary>
        /// <typeparam name="TValidator">The type of validator.</typeparam>
        public bool IsRegister<TValidator>()
        {
            using (DeadLockDetector.Lock(_locker))
            {
                return _validatorPrototypes.ContainsKey(typeof(TValidator));
            }
        }

        /// <summary>
        ///     Gets the series of validators for the specified instance.
        /// </summary>
        /// <param name="instanceToValidate">The specified instance to validate.</param>
        /// <returns>A series instances of validators.</returns>
        public IList<IValidator> GetValidators(object instanceToValidate)
        {
            Should.NotBeNull(instanceToValidate, "instanceToValidate");
            return GetValidators(new ValidatorContext(instanceToValidate, ServiceProvider));
        }

        /// <summary>
        ///     Gets the series of validators for the specified instance.
        /// </summary>
        /// <param name="context">The specified IValidatorContext.</param>
        /// <returns>A series instances of validators.</returns>
        public IList<IValidator> GetValidators(IValidatorContext context)
        {
            Should.NotBeNull(context, "context");
            using (DeadLockDetector.Lock(_locker))
            {
                var listResults = new List<IValidator>();
                foreach (IValidator value in _validatorPrototypes.Values.SelectMany(list => list))
                {
                    if (!value.CanValidate(context)) continue;
                    IValidator validator = value.Clone();
                    validator.Initialize(context);
                    listResults.Add(validator);
                }
                return listResults;
            }
        }

        /// <summary>
        ///     Creates a new validator-factory that is a copy of the current instance.
        /// </summary>
        /// <returns>
        ///     A new validator-factory that is a copy of this instance.
        /// </returns>
        public IValidatorProvider Clone()
        {
            using (DeadLockDetector.Lock(_locker))
            {
                return new ValidatorProvider(ServiceProvider)
                           {
                               _validatorPrototypes =
                                   _validatorPrototypes
                                   .ToDictionary(pair => pair.Key, pair => pair.Value.ToList())
                           };
            }
        }

        #endregion
    }
}